package com.baranbaygan.downloadmanager
{
	import com.baranbaygan.downloadmanager.events.DownloadPackageEvent;
	import com.baranbaygan.downloadmanager.events.DownloadPackageProgressEvent;
	import com.baranbaygan.downloadmanager.events.DownloadStatusEvent;
	import com.baranbaygan.downloadmanager.events.FileDownloadEvent;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	
	import mx.collections.ArrayCollection;

	public class DownloadPackage extends EventDispatcher
	{
		private var _bytesCompleted:Number=0;
		private var _bytesTotal:Number=0;
		private var _speed:Number=0;
		private var _packageName:String;
		private var _packageId:String;
		private var _status:String;
		 
		private var _downloadFiles:ArrayCollection = new ArrayCollection(new Array());
		private var _fileDownloadManager:FileDownloadManager;
		
		private var _externalDataXML:XML;
		
		
		public function get BytesCompleted():Number{ return this._bytesCompleted; }
		public function get BytesTotal():Number{ return this._bytesTotal; }
		public function get Speed():Number{ return this._speed; }
		public function get PackageID():String{ return this._packageId; }
		public function get PackageName():String{ return this._packageName; }
		public function get Status():String{ return this._status; }
		
		[Bindable(event=DownloadStatusEvent.StatusChange)]
		public function set Status(value:String):void
		{ 
			var changed:Boolean=false;
			if(this._status != value)
				changed=true;

			this._status=value;
			
			if(changed)
			//if(true)
			{
				var e:DownloadStatusEvent = new DownloadStatusEvent(DownloadStatusEvent.StatusChange);
				e.Status = this._status;
				this.dispatchEvent(e);
			}
		}
		
		public function get ExternalDataXML():XML
		{
			return this._externalDataXML;
		}
		public function get DownloadFiles():ArrayCollection{ return this._downloadFiles; }
		
		public function DownloadPackage(downloadPackageXml:XML, fileDownloadManager:FileDownloadManager)
		{
			super(null);
			
			_fileDownloadManager = fileDownloadManager;
			
			_packageId = downloadPackageXml.PackageID.toString();
			_packageName = downloadPackageXml.PackageName.toString();
			_status = downloadPackageXml.Status.toString();
			_externalDataXML = downloadPackageXml.ExternalData[0];
			
			this.addEventListener(DownloadStatusEvent.StatusChange, Status_OnChange);
			
			for each(var fileXml:XML in downloadPackageXml.file)
			{
				var dFile:DownloadFile = new DownloadFile(fileXml, fileDownloadManager, this);
				
				dFile.addEventListener(Event.OPEN, DownloadFile_OnOpen);
				dFile.addEventListener(ProgressEvent.PROGRESS, DownloadFile_OnProgress);
				dFile.addEventListener(Event.COMPLETE, DownloadFile_OnComplete);
				
				_downloadFiles.addItem(dFile);
			}
		}
		
		/**
		 * This sums the loaded and total bytes.
		 */ 
		private function CalculateProgress():void
		{
			this._bytesCompleted = 0;
			this._bytesTotal = 0;
			
			for each(var file:DownloadFile in this._downloadFiles)
			{
				this._bytesCompleted += file.CompletedBytes;
				this._bytesTotal += file.TotalBytes;
			}
		}

		protected function DownloadFile_OnProgress(event:ProgressEvent):void
		{
			CalculateProgress();
			
			this._status = DownloadStatus.DOWNLOADING;
			
			var e:DownloadPackageProgressEvent = new DownloadPackageProgressEvent(DownloadPackageProgressEvent.DownloadPackageProgress);
			e.EventTargetDownloadFile = DownloadFile(event.target);
			e.TargetDownloadPackage = this;
			e.BytesTotal = event.bytesTotal;
			e.BytesCompleted = event.bytesLoaded;

			this.dispatchEvent(e);
		}

		protected function DownloadFile_OnOpen(event:Event):void
		{
			this._status = DownloadStatus.DOWNLOADING;
			
			var e:DownloadPackageEvent = new DownloadPackageEvent(Event.OPEN);
			e.EventTargetDownloadFile = DownloadFile(event.target);
			e.TargetDownloadPackage = this;
			this.dispatchEvent(e);
		}

		protected function DownloadFile_OnComplete(event:Event):void
		{
			// Dispatch a file download event. This is important for the downloadtracker.
			var fileDownloadEvent:FileDownloadEvent = new FileDownloadEvent(FileDownloadEvent.FILE_COMPLETE);
			fileDownloadEvent.TargetDownloadPackage = this
			fileDownloadEvent.FileObject = DownloadFile(event.target);
			this.dispatchEvent(fileDownloadEvent);
			
			// Now find out if all the files has been downloaded in this package.
			for each(var df:DownloadFile in this._downloadFiles)
			{
				if(df.Status != DownloadStatus.COMPLETED)
				{
					return;
				}
			}
			
			// All the files have finished downloading here.
			
			this._status = DownloadStatus.COMPLETED;
			
			var e:DownloadPackageEvent = new DownloadPackageEvent(Event.COMPLETE);
			e.EventTargetDownloadFile = DownloadFile(event.target);
			e.TargetDownloadPackage = this;
			this.dispatchEvent(e); 
		}
		
		/**
		 * This event is registered in the configureDownloadPackageListeners() function in FileDownloadManager
		 */ 
		public function Timer_Tick(event:TimerEvent):void
		{
			if(this.Status == DownloadStatus.QUEUED || this.Status == DownloadStatus.PAUSED)
				return;

			var downloadingFileCount:Number=0;
			this._speed = 0;
			for each(var df:DownloadFile in this._downloadFiles)
			{
				if(df.Status == DownloadStatus.DOWNLOADING)
				{
					downloadingFileCount++;
					df.Timer_Tick();
					this._speed += df.Speed;
				}
			}
			
			if(downloadingFileCount == 0)
			{
				// We don't have a file downloading. Lets start a queued file.
				StartAQueuedFile();
			}
		}
		
		private function StartAQueuedFile():void
		{
			for each(var df:DownloadFile in this._downloadFiles)
			{
				if(df.Status == DownloadStatus.QUEUED)
				{
					df.StartDownload();
					this.Status = DownloadStatus.DOWNLOADING;
					return;
				}
			}
		}
		
		public function RemoveDownloadedFiles():void
		{
			for each(var df:DownloadFile in this._downloadFiles)
			{
				df.DeleteDownloadedFile();
			}
			
			// To-DO Delete package folder here
		}
		
		public function GetDownloadPackageXML():XML
		{
			var packageXml:XML = <DownloadPackage/>;

			packageXml.PackageName = _packageName;
			packageXml.PackageID = _packageId;
			packageXml.Status = this._status;
			packageXml.Speed = _speed.toString();
			packageXml.BytesCompleted = this._bytesCompleted;
			packageXml.BytesTotal = this._bytesTotal;
			packageXml.ExternalData = this._externalDataXML[0];

			for each(var dFile:DownloadFile in this._downloadFiles)
			{
				var fileXml:XML = <file/>;
				fileXml.Url = dFile.Url;
				fileXml.FileName = dFile.FileName;
				fileXml.FileId = dFile.FileId;
				fileXml.Status = dFile.Status;
				fileXml.CompletedBytes = dFile.CompletedBytes.toString();
				fileXml.TotalBytes = dFile.TotalBytes.toString();
				fileXml.Speed = dFile.Speed.toString();
				fileXml.appendChild(dFile.ExternalData);
				packageXml.appendChild(fileXml);
			}
			
			return packageXml;
		}
		
		private function GetDownloadFile(fileId:String):DownloadFile
		{
			for each(var df:DownloadFile in this._downloadFiles)
			{
				if(df.FileId == fileId)
					return df;
			}
			return null;
		}
		
		protected function Status_OnChange(event:DownloadStatusEvent):void
		{
			switch(event.Status)
			{
				case DownloadStatus.PAUSED:
				{
					this.PauseDownload();
					break;
				}
				case DownloadStatus.DOWNLOADING:
				{
					this.StartDownload();
					break;
				}
			}
		}
		
		public function StartDownload(fileId:String=""):void
		{
			if(fileId=="")
			{
				// Start all the files in this package
				for each(var df:DownloadFile in this._downloadFiles)
				{
					if(df.Status != DownloadStatus.COMPLETED)
					{
						// Lets make the status of the uncompleted files QUEUED.
						// This will trigger them to get started one by one when the timer ticks.
						df.Status = DownloadStatus.QUEUED;
					}
				}
			}
			else
			{
				// Start only the specified file in the parameter
				GetDownloadFile(fileId).StartDownload();
			}
		}
		private function PauseDownload(fileId:String=""):void
		{
			if(fileId=="")
			{
				// Pause all the files in this package
				for each(var df:DownloadFile in this._downloadFiles)
				{
					df.PauseDownload();
				}
				
				this._status = DownloadStatus.PAUSED;
			}
			else
			{
				// Pause only the specified file in the parameter
				GetDownloadFile(fileId).PauseDownload();
			}
		}
		public function CancelDownload(fileId:String=""):void
		{
			if(fileId=="")
			{
				// Cancel all the files in this package
				for each(var df:DownloadFile in this._downloadFiles)
				{
					df.CancelDownload();
				}
			}
			else
			{
				// Cancel only the specified file in the parameter
			}
		}
	}
}